{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "aad61277-d251-4b6d-b24a-0f2a36b4a835",
   "metadata": {},
   "source": [
    "## Exercise Set 1\n",
    "\n",
    "### Multiple Choice \n",
    "**Q1.** Which of these are NOT key components for an optimization model?\n",
    "- decision variables\n",
    "- parameters\n",
    "- objective function\n",
    "- `hyperparameters`"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "23a353af-3b6c-4bf6-8aad-6c6517192496",
   "metadata": {},
   "source": [
    "**Q2.** MO can be used as a strict replacement for nearly all ML problems\n",
    "- True\n",
    "- `False`"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "1fc1f1ff-1a52-44a0-8238-218e768cd030",
   "metadata": {},
   "source": [
    "**Q3.** ML can be used as a strict replacement for nearly all MO problems\n",
    "- True\n",
    "- `False`"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "ef95ad3e-ea49-4d3c-9a97-138731da48a8",
   "metadata": {},
   "source": [
    "**Q4.** Which of the following is a type of decision variables in mathematical optimization?\n",
    "- `binary`\n",
    "- complex\n",
    "- feature\n",
    "- complimentary"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "bd09ddc9-372b-4162-ae4d-c33faffc7324",
   "metadata": {},
   "source": [
    "**Q5.** To solve a mixed-integer program, a solver may need to solve this many linear programs:\n",
    "- none\n",
    "- the number of decision variables\n",
    "- the number of total constraints\n",
    "- `possibly exponentially many`"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "fb2bcfd8-7eee-4a17-aebf-8c42aab69d2e",
   "metadata": {},
   "source": [
    "**Q6.** It is required that you or your data analytics team develop an expertise in `predictive` analytics before attempting `prescriptive` analytics (like mathematical optimization).\n",
    "- True\n",
    "- `False`"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "b8d30a5d-29e7-4fd3-97ca-5f32ca2bb257",
   "metadata": {},
   "source": [
    "**Q7.** The `feasible region` of a linear program (LP) will have ____ points in it than its corresponding mixed-integer program (MIP), assuming the two models are exactly the same other than the LP has only continuous variables and the MIP contains integer variables.\n",
    "- `more`\n",
    "- less\n",
    "- always exactly the same"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "5e67fb56-2e80-4efe-a604-68dda572c41d",
   "metadata": {},
   "source": [
    "**Q8.** Unless specified, the default variable type when using `addVars()` is **continuous**.\n",
    "- `True`\n",
    "- False"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "f97876b8-eb9b-4191-8820-a3d635dda8ea",
   "metadata": {},
   "source": [
    "Let $J = \\{\\texttt{Apple, Banana, Coconut, Dragonfruit, Elderberry, Fig, Gooseberry}\\}$ and $T = \\{1, 2, 3, 4\\}$ \n",
    "\n",
    "**Q9-a.** Adding decision variables using `addVars(J,...)` and `addVars(range(8),...)` will add the *same* number of variables to a model\n",
    "- True\n",
    "- `False`\n",
    "\n",
    "**Q9-b.** Using the sets above, adding decision variables using `addVars(J, T,...)` and `addVars(range(28),...)` will add the *same* number of variables to a model\n",
    "- `True`\n",
    "- False"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "39eeaee0-628e-46f3-9fd9-f74ce324eefe",
   "metadata": {},
   "source": [
    "### Formulation and Coding \n",
    "Below is code for the entire original model in one cell if you would like to use it to help with these exercises."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "b963b996-8e54-448f-be9d-36822d984b22",
   "metadata": {},
   "outputs": [],
   "source": [
    "%pip install gurobipy\n",
    "\n",
    "import pandas as pd\n",
    "import gurobipy as gp\n",
    "from gurobipy import GRB\n",
    "\n",
    "production = ['Baltimore','Cleveland','Little Rock','Birmingham','Charleston']\n",
    "distribution = ['Columbia','Indianapolis','Lexington','Nashville','Richmond','St. Louis']\n",
    "\n",
    "path = 'https://raw.githubusercontent.com/Gurobi/modeling-examples/master/optimization101/Modeling_Session_1/'\n",
    "transp_cost = pd.read_csv(path + 'cost.csv', index_col=[0,1]).squeeze(\"columns\")\n",
    "max_prod = pd.Series([180,200,140,80,180], index = production, name = \"max_production\")\n",
    "n_demand = pd.Series([89,95,121,101,116,181], index = distribution, name = \"demand\") \n",
    "frac = 0.75\n",
    "\n",
    "m = gp.Model('widgets')\n",
    "x = m.addVars(production, distribution, name = 'prod_ship')\n",
    "meet_demand = m.addConstrs((gp.quicksum(x[p,d] for p in production) >= n_demand[d] for d in distribution), name = 'meet_demand')\n",
    "can_produce = m.addConstrs((gp.quicksum(x[p,d] for d in distribution) <= max_prod[p] for p in production), name = 'can_produce')\n",
    "must_produce = m.addConstrs((gp.quicksum(x[p,d] for d in distribution) >= frac*max_prod[p] for p in production), name = 'must_produce')\n",
    "m.setObjective(gp.quicksum(transp_cost[i,j]*x[i,j] for i in production for j in distribution), GRB.MINIMIZE) \n",
    "m.optimize()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "218720a0-d7aa-4f28-ac07-e1318d62305a",
   "metadata": {},
   "source": [
    "You are told there is a new policy for transporting **widgets** from production facilities. It is now required that the minimum number of widgets shipped from any production facility to any distribution center needs to be at least 20. \n",
    "\n",
    "**Q10-a.** Write out how the formulation changes using mathematical notation given the new requirement."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "6ae7c7e0-9eff-4a29-a033-a2a2c2328edb",
   "metadata": {},
   "source": [
    "\\begin{align*} \n",
    "x_{p,d} \\ge 20, \\quad \\forall p \\in P, d \\in D\n",
    "\\end{align*}"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "3b00d486-0ca4-4382-96f7-a817cf5d0ac9",
   "metadata": {},
   "source": [
    "**Q10-b.** Write the changes for **Q10-a** in gurobipy code in the cell below (no need to run it, unless you want to copy the model here to check)."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "345a0844-83b7-4ec0-87ea-3f3d14a11c26",
   "metadata": {},
   "outputs": [],
   "source": [
    "m.addConstrs(x[p,d] >= 20 for p in production for d in distribution)\n",
    "m.update()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "c60c5e1b-d9b5-4389-9d00-cb525da52c61",
   "metadata": {},
   "source": [
    "The initial widget model $m$ represented a single time period (e.g. a week, month, quarter). Suppose we added a time component using a set $T = \\{0, 1, 2, 3\\}$ representing a quarter of a year. \n",
    "\n",
    "**Q11-a.** Use `addVar()` or `addVars()` to create a decision variable in gurobipy that represents the number of widgets shipped from a production facility to a distribution center for a given time period. "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "673d6901-853b-4947-b29f-7dcca25fa10a",
   "metadata": {},
   "outputs": [],
   "source": [
    "x = m.addVars(production, distribution, range(4), name = 'prod_ship')"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "7d3cd726-e115-42ff-a1f8-b6d11bfec3b9",
   "metadata": {},
   "source": [
    "**Q11-b.** To reference a time period before a given time $t$, we can use $t-1$ as a subscript (since $T$ is a set of integers), but $t-1$ doesn't work when $t = 0$ since $-1$ isn't in $T$. Fill in the ??? in the code below to represent a set of constraints that limits the amount a production facility can produce to `max_prod[p]` over *two consecutive* time periods. "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "e48ef0e5-7ff7-456f-b587-d7140c5b3ee8",
   "metadata": {},
   "outputs": [],
   "source": [
    "time_prod_limit = m.addConstrs((gp.quicksum(x[p,d,???] + x[p,d,???] for d in distribution) <= max_prod[p] for p in production for t in ??? if t ???), name = 'time_prod_limit')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "a4e9a074-2b0f-4d94-8902-96f8b10e6e40",
   "metadata": {},
   "outputs": [],
   "source": [
    "time_prod_limit = m.addConstrs((gp.quicksum(x[p,d,t] + x[p,d,t-1] for d in distribution) <= max_prod[p] for p in production for t in range(4) if t > 0), name = 'time_prod_limit')"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3 (ipykernel)",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.9.7"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
